﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SlimDX.XInput;
using SlimDX;
using System.Threading;

namespace Apparat
{
    public class GamePad
    {
        SlimDX.XInput.Controller controller;
        Thread gamepadThread;
        State state;


        bool polling = true;
        //bool connected = true;

        uint lastPacket;
        public DPadState DPad { get; private set; }
        public ThumbstickState LeftStick { get; private set; }
        public ThumbstickState RightStick { get; private set; }

        public bool A { get; private set; }
        public bool B { get; private set; }
        public bool X { get; private set; }
        public bool Y { get; private set; }

        public bool RightShoulder { get; private set; }
        public bool LeftShoulder { get; private set; }

        public bool Start { get; private set; }
        public bool Back { get; private set; }

        public float RightTrigger { get; private set; }
        public float LeftTrigger { get; private set; }

        public bool Connected { get; private set; }

        public delegate void GamePadUpdateHandler();
        public event GamePadUpdateHandler gamePadUpdateEvent;



        private static GamePad instance = null;
        public static GamePad Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new GamePad();
                }
                return instance;
            }
        }



        private GamePad()
        {

        }

        public void Init()
        {
            controller = new Controller(0);
            ThreadStart ts = new ThreadStart(pollingLoop);
            gamepadThread = new Thread(ts);
            gamepadThread.Start();
        }

        public void ShutDown()
        {
            //if (connected)
            polling = false;

            //if (gamepadThread != null)
            //    gamepadThread.Join();
        }


        public void pollingLoop()
        {
            while (polling)
            {
                try
                {
                    if (!controller.IsConnected)
                    {
                        Thread.Sleep(1000);
                        Connected = false;
                        if (gamePadUpdateEvent != null)
                            gamePadUpdateEvent();
                        continue;
                    }
                    else
                    {
                        Connected = true;
                    }



                    state = controller.GetState();


                    var gamepadState = state.Gamepad;
                    uint num = state.PacketNumber;

                    //Console.WriteLine(num.ToString());

                    if (num != lastPacket)
                    {
                        lastPacket = num;
                    }
                    //else
                    //{
                    //    System.Windows.Forms.MessageBox.Show("same packet");
                    //}

                    // Shoulders
                    LeftShoulder = (gamepadState.Buttons & GamepadButtonFlags.LeftShoulder) != 0;
                    RightShoulder = (gamepadState.Buttons & GamepadButtonFlags.RightShoulder) != 0;

                    // Triggers
                    LeftTrigger = gamepadState.LeftTrigger / (float)byte.MaxValue;
                    RightTrigger = gamepadState.RightTrigger / (float)byte.MaxValue;

                    // Buttons
                    Start = (gamepadState.Buttons & GamepadButtonFlags.Start) != 0;
                    Back = (gamepadState.Buttons & GamepadButtonFlags.Back) != 0;

                    A = (gamepadState.Buttons & GamepadButtonFlags.A) != 0;
                    B = (gamepadState.Buttons & GamepadButtonFlags.B) != 0;
                    X = (gamepadState.Buttons & GamepadButtonFlags.X) != 0;
                    Y = (gamepadState.Buttons & GamepadButtonFlags.Y) != 0;

                    // D-Pad
                    DPad = new DPadState((gamepadState.Buttons & GamepadButtonFlags.DPadUp) != 0,
                                         (gamepadState.Buttons & GamepadButtonFlags.DPadDown) != 0,
                                         (gamepadState.Buttons & GamepadButtonFlags.DPadLeft) != 0,
                                         (gamepadState.Buttons & GamepadButtonFlags.DPadRight) != 0);

                    // Thumbsticks
                    LeftStick = new ThumbstickState(
                        Normalize(gamepadState.LeftThumbX, gamepadState.LeftThumbY, Gamepad.GamepadLeftThumbDeadZone),
                        (gamepadState.Buttons & GamepadButtonFlags.LeftThumb) != 0);
                    RightStick = new ThumbstickState(
                        Normalize(gamepadState.RightThumbX, gamepadState.RightThumbY, Gamepad.GamepadRightThumbDeadZone),
                        (gamepadState.Buttons & GamepadButtonFlags.RightThumb) != 0);

                    if (gamePadUpdateEvent != null)
                        gamePadUpdateEvent();
                }
                catch (Exception e)
                {
                    //Debug.WriteLine(e.Message);
                }

                Thread.Sleep(10);
            }
        }

        static Vector2 Normalize(short rawX, short rawY, short threshold)
        {
            var value = new Vector2(rawX, rawY);
            var magnitude = value.Length();
            var direction = value / (magnitude == 0 ? 1 : magnitude);

            var normalizedMagnitude = 0.0f;
            if (magnitude - threshold > 0)
                normalizedMagnitude = Math.Min((magnitude - threshold) / (short.MaxValue - threshold), 1);

            return direction * normalizedMagnitude;
        }

        public struct DPadState
        {
            public readonly bool Up, Down, Left, Right;

            public DPadState(bool up, bool down, bool left, bool right)
            {
                Up = up; Down = down; Left = left; Right = right;
            }
        }

        public struct ThumbstickState
        {
            public readonly Vector2 Position;
            public readonly bool Clicked;

            public ThumbstickState(Vector2 position, bool clicked)
            {
                Clicked = clicked;
                Position = position;
            }
        }
    }
}
